/**
 * @file magc.c
 * @brief T1 Magnetic Card Reader Driver
 *
 * @author yemt@witsi.cn
 * @date 2012-04-10
 * @version T1.UART.04
 */
#include "arch.h"
#include "magc.h"
#include "config.h"
#include "ictl.h"
#include "iodev.h"
#include <string.h>
#include <sys/ioctl.h>
#include "syscfg.h"
#include "autoconfig.h"
#include "kernel.h"
#include "running.h"

#define STX1 0x45
#define ETX1 0x1F
#define STX2 0x0B
#define ETX2 0x1F

/************************************************************************
 * Magnetic Card Track Bits Data
 ************************************************************************/
#define MAG_MAX_BITS    1024
#define MAG_MAX_LONG    ((MAG_MAX_BITS + 31) / 32)
typedef struct {
        uint32_t data[MAG_MAX_LONG];
        uint32_t bits_count;
}track_data_t;
static track_data_t tdata[3];

#define MAG_PUSLES_LEN 1024
typedef struct {
	uint16_t data[MAG_PUSLES_LEN];
	uint16_t count;
}track_pulse_t;

static track_pulse_t pluse_buf[3];
static volatile uint32_t track_flags;

#define TRACK_DATA_READY	0x001
#define TRACK_DATA_START	0x100
#define TRACK_DATA_MASK		0x111
#define TRACK_SET_ONE(tr, n)   \
        (tr)->data[(n) >> 5] |= (1 << ((n) & 31))
#define TRACK_OK(flag)		(flag && ((flag&0x07)==((flag>>8)&0x07)))
#define TRACK_N_OK(n, flag) (flag && ((flag&(0x01<<(n)))==((flag>>8)&(0x01<<(n)))))
#define TRACK_VALID(n) (track_flags & (TRACK_DATA_READY << n)) /* track n (0,1,2) valid */

static uint32_t trackAdaptDone = 0;
static uint32_t track1 = 0;
static uint32_t track2 = 1;
static uint32_t track3 = 2;



/************************************************************************
 * Magnetic Card Track Sampling IRQ
 ************************************************************************/
static void magnetic_irq(void)
{
	uint32_t temp, i, pulse;
	uint32_t t0, t1;

	temp = MAGC->INT_STA;
	t0 = read_c0_count();
	t1 = 0;
	track_flags = 0;
	do {
		MAGC->INT_CLR = temp;
		for (i = 0; i < 3; i ++) {
			pulse = MAGC->RD_REG[i]; /* Pulse Width */
			if (temp & (MAG_INT_PULSE_A << i)) {
				if ((track_flags & (TRACK_DATA_START << i)) == 0) {
					goto discard;
				}
				if (pluse_buf[i].count == MAG_PUSLES_LEN) {
					goto discard;
				}
				if (track_flags & (TRACK_DATA_READY << i)) {
					goto _continue;
				}
				pluse_buf[i].data[pluse_buf[i].count++] = pulse;
			}
			else if (temp & (MAG_INT_DATA_START_A << i)) {
				if (track_flags & (TRACK_DATA_START << i)) {
					// FIXME: maybe insert a largest PULSE
				}
				else {
					pluse_buf[i].data[0] = pulse;
					pluse_buf[i].count = 1;
					track_flags &= ~(TRACK_DATA_MASK << i);
					track_flags |= TRACK_DATA_START << i;
				}
			}
			else if (temp & (MAG_INT_DATA_OVER_A << i)) {
				if ((track_flags & (TRACK_DATA_START << i)) == 0) {
					goto discard;
				}
				if (pluse_buf[i].count < 20) {
					goto _continue;
				}
				else {
					track_flags |= (TRACK_DATA_READY << i);
				}
			}
		}
_continue:
		temp = MAGC->INT_STA;
		if(temp) {
			t0 = read_c0_count();
		} 
		else {
			#define __TO	((cp0info_cur_freq() / 1000) * 100) // 100ms
			if((read_c0_count() - t0) > __TO) {
				goto discard;
			}
		}
	}while(!TRACK_OK(track_flags));

    for (i=0; i<3; i++) {
        if (pluse_buf[i].count < 100) track_flags &= ~(TRACK_DATA_MASK<<i);
    }

    if (TRACK_OK(track_flags)) MAGC->INT_EN = 0; /* disable magc interrupt */
	return ;
discard:
	track_flags = 0;
	return ;
}

#define WINDOW_LEN      8
#define INIT_SLIDINGWINDOW(val) \
do{\
    for (k=0; k<WINDOW_LEN; k++) window[k] = val;\
}while(0)

#define UPDATE_T0(t0, val) \
do{\
    windowTmp = window[7] = (val);\
    for (k=0; k<WINDOW_LEN-1; k++) {\
        window[k] = window[k+1];\
        windowTmp += window[k];\
    }\
    windowTmp >>= 3;\
    t0 = (uint16_t)windowTmp;\
}while(0)

#define ABANDON_BITS    5 /* note: ABANDON_BITS > 2*/
#define LONG_PULSE(n)   ((n) > tresholdVaule)
#define CRISIS_PULSE(n) ((n) == tresholdVaule)
#define NARROW_PULSE(n) ((n) < tresholdVaule)
static void magc_bits_decode(void)
{
	uint32_t i, j, index;
	uint16_t Tc; /* Time of the current pusle cycle */
	uint16_t Tn; /* Time of the next pusle cycle */
	uint16_t Tt; /* Time of the third cycle */
	uint16_t T0; /* Time of the logic 0 pulse cycle */
	uint16_t tresholdVaule;
	uint32_t bit;

	static uint16_t window[8]; /* sliding window */
	uint32_t windowTmp;
	uint16_t k;

	
	for (i = 0; i < 3; i++) {
		if (TRACK_N_OK(i, track_flags)) {
			memset(tdata+i, 0x00, sizeof (track_data_t));
			tdata[i].bits_count = 1;

			if (pluse_buf[i].count < 100){ /*  */
next_track:
				continue;
			}

			/* caculate T0 */
			T0 = 0;
			for (j = 0; j < 8; j++) {
				Tc = pluse_buf[i].data[j+1];
				Tn = pluse_buf[i].data[j+2];
				if (Tc > (Tn<<1)) {
					Tc = (pluse_buf[i].data[j] + Tn) >> 1;
				}
				T0 += Tc;
			}
			T0 = T0 >> 3;
			INIT_SLIDINGWINDOW(T0);
			tresholdVaule = T0 - (T0 >> 2);

			index = ABANDON_BITS;
			pluse_buf[i].count -= ABANDON_BITS + 2; /* 2bit reserved  */

			for ( ; index < pluse_buf[i].count; index++) {
				Tc = pluse_buf[i].data[index];
				Tn = pluse_buf[i].data[index+1]; /* only when ABANDON_BITS > 0 */

				if (LONG_PULSE(Tc)) {
					bit = 0;
					for (j=0; j+1<Tc/T0; j++) {
						/* set logic 0 */
						tdata[i].bits_count++;
						if(tdata[i].bits_count >= MAG_MAX_BITS) {
							goto next_track;// break;
						}
					}
				}
				else {
					bit = 1;
					if (CRISIS_PULSE(Tc)) {
						if (LONG_PULSE(Tn)||CRISIS_PULSE(Tn)) {
							bit = 0;
						}
					}
					else {
						if (NARROW_PULSE(Tc+Tn) || CRISIS_PULSE(Tc+Tn)) {
							Tt = pluse_buf[i].data[index+2]; /* only when ABANDON_BITS > 2 */
							if (NARROW_PULSE(Tt)) {
								bit = 0;
								index += 2;
								Tc += Tn + Tt;
							}
						}
						if ((Tc+Tn) > (T0+(T0>>1)) || (Tc+Tn) <= (T0>>1)) {  /* abs(Tc + Tn - T0) >= (T0>>1) */
               bit = 0;
            }
					}
				}

				if (bit==1) {
					TRACK_SET_ONE(tdata + i, tdata[i].bits_count);
					index++;
					//T0 = (T0 + Tc + Tn)>>1;
					UPDATE_T0(T0, (Tc+Tn));
				}
				else {
					//T0 = (T0 + Tc)>>1;
					UPDATE_T0(T0, Tc);
				}
				tdata[i].bits_count ++;
				if(tdata[i].bits_count >= MAG_MAX_BITS) {
					goto next_track;// break;
				}
				
				tresholdVaule = T0 - (T0 >> 2);
			}

		}
	}

}

/*
** BRIEF:
**		Reversal byte
** PARAM:
**    data[in] data to reversal
** RETURN:
**	  the reversaled data
*/
static uint32_t reversal_32bit(uint32_t data)
{
	uint32_t i;
	uint32_t tmp;

	tmp = 0;
	for (i=0x80000000; i>0; i>>=1) {
		tmp >>= 1;
		if ((data&i)!=0) {
			tmp |= 0x80000000;
		}
	}
	return (tmp);
}


/* 
  * Parity(odd, b6) b5 b4 b3 b2 b1 b0
  *   return :
  *               0 success
  *               -1 failure
  */
static int track1_parity(uint32_t data)
{
    int i, parity = 0;
    
    for (i=0x01; i<0x80; i<<=1) {
        if (data&i) parity++;
    }
    
    if (parity&0x01) return 0;

    return -1;
}


/*
** BRIEF:
**		decode track1 data
** PARAM:
**    trackBuf[in] src data
**		trackOut[out] decoded data to be stored;
**		trackOutLen[in] the length of trackOut
** RETURN:
**   >0 decode success, return the length of data
**   -1 invalid data
**   -2 does not get ETX
**   -3 LRC error
**   -4 does not get STX
**   -5 parity err
*/

#define INVALID_DATA  -1
#define NOT_GET_ETX   -2
#define LRC_ERROR     -3
#define NOT_GET_STX   -4
#define PARITY_ERR    -5
static int track1_decode(uint32_t *trackIn, uint8_t trackInLen,\
                          uint8_t *trackOut, uint8_t trackOutLen)
{
	uint16_t indexBit;
	uint8_t  indexTrackOut;
	uint32_t tmp;
	uint8_t  i;
	uint32_t xorLrc;
	uint8_t  getETX;
	uint8_t  reverse_done = 0;
	uint16_t indexBitSTX;
    uint8_t parityErr;

repeat:

	indexBit = 0;
	indexTrackOut = 0;
	xorLrc = 0;
	getETX = 0;
	i = 0;
    parityErr = 0;

	while ( trackIn[i]== 0) { /*find logic 1 */
		i++;
		indexBit += 32;
		if (i >= trackInLen) {
			return (INVALID_DATA);
		}
	}

	tmp = trackIn[i];
	for (i=0; i<32; i++) {
		if ((tmp&0x01) != 0) {
			indexBit += i;
			break;
		}
		tmp >>= 1;
	}

	tmp = trackIn[indexBit>>5]>>(indexBit&31);
	if ((indexBit&31) > 25) {
		tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
	}

	while ((tmp&0x7f) != STX1){ /* find out STX1 */
		indexBit++;
		if (indexBit >= 20) {
			goto reverse;
		}
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 25) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}
	}

	indexBitSTX = indexBit;
	for ( ; indexBit<(trackInLen<<5); indexBit+=7) {
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 25) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}

        if (track1_parity(tmp) < 0) parityErr = 1;

		if (getETX) {
			if ((indexBit - indexBitSTX) == 14) {
				goto reverse;
			}
			if ((tmp&0x3f) == xorLrc) {
                if (parityErr) return PARITY_ERR;
				return (indexTrackOut);
			}
			else {
				if (!reverse_done) {
					goto reverse;
				}
				return (LRC_ERROR);
			}
		}

		if (indexTrackOut < trackOutLen) {
			trackOut[indexTrackOut++] = (unsigned char)(tmp&0x3f) + ' '; /* turn into ASCII */
		}
		xorLrc ^= (tmp&0x3f);

		if ((tmp&0x7f) == ETX1) {
			getETX = 1;
		}
	}
	if (reverse_done) {
		return (NOT_GET_ETX);
	}

reverse:
	if (!reverse_done) { /* reverse direction */
		for (i=0; i<(trackInLen>>1); i++) {
			tmp = trackIn[i];
			trackIn[i] = reversal_32bit(trackIn[trackInLen-1-i]);
			trackIn[trackInLen-1-i] = reversal_32bit(tmp);
		}
		if ((trackInLen&1) == 1) {
			tmp = trackIn[trackInLen>>1];
			trackIn[trackInLen>>1] = reversal_32bit(tmp);
		}
		reverse_done = 1;
		goto repeat;
	}
	else {
		return (NOT_GET_STX);
	}
}

/* 
  * Parity(odd, b4) b3 b2 b1 b0
  *   return :
  *               0 success
  *               -1 failure
  */
static int track23_parity(uint32_t data)
{
    int i, parity = 0;
    
    for (i=0x01; i<0x20; i<<=1) {
        if (data&i) parity++;
    }
    
    if (parity&0x01) return 0;

    return -1;
}

/*
** BRIEF:
**		decode track2 or track3 data
** PARAM:
**    trackBuf[in] src data
**		trackOut[out] decoded data to be stored
**		trackOutLen[in] the length of trackOut
** RETURN:
**   >0 decode success, return the length of data
**   -1 invalid data
**   -2 does not get ETX
**   -3 LRC error
**   -4 does not get STX
**   -5 parity error
*/
static int track23_decode(uint32_t *trackIn, uint8_t trackInLen,\
                           uint8_t *trackOut, uint8_t trackOutLen)
{
	uint16_t indexBit;
	uint8_t indexTrackOut;
	uint32_t tmp;
	uint8_t i;
	uint32_t xorLrc;
	uint8_t getETX;
	uint8_t reverse_done = 0;
	uint16_t indexBitSTX;
    uint8_t parityErr;

repeat:

	indexBit = 0;
	indexTrackOut = 0;
	xorLrc = 0;
	getETX = 0;
	i = 0;
    parityErr = 0;

	while ( trackIn[i]== 0) { /* find logic 1 */
		i++;
		indexBit += 32;
		if (i >= trackInLen) {
			return (INVALID_DATA);
		}
	}

	tmp = trackIn[i];
	for (i=0; i<32; i++) {
		if ((tmp&0x01) != 0) {
			indexBit += i;
			break;
		}
		tmp >>= 1;
	}

	tmp = trackIn[indexBit>>5]>>(indexBit&31);
	if ((indexBit&31) > 27) {
		tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
	}

	while ((tmp&0x1f) != STX2){ /* find out STX2 */
		indexBit++;
		if (indexBit >= 40) {
			goto reverse;
		}
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 27) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}
	}

	/* forward direction */
	indexBitSTX = indexBit;
	for ( ; indexBit<(trackInLen<<5); indexBit+=5) {
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 27) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}

        if (track23_parity(tmp) < 0) parityErr = 1;
        
		if (getETX) {
			if ((indexBit - indexBitSTX) == 10) {
				goto reverse;
			}
			if ((tmp&0x0f) == xorLrc) {
                if (parityErr) return PARITY_ERR;
				return (indexTrackOut);
			}
			else {
				if (!reverse_done) {
					goto reverse;
				}
				return (LRC_ERROR);
			}
		}

		if (indexTrackOut < trackOutLen) {
			trackOut[indexTrackOut++] = (unsigned char)(tmp&0x0f) + '0'; /* turn into ASCII */
		}
		xorLrc ^= (tmp&0x0f);

		if ((tmp&0x1f) == ETX2) {
			getETX = 1;
		}
	}
	if (reverse_done) {
		return (NOT_GET_ETX);
	}

reverse:
	if (!reverse_done) { /* reverse direction */
			for (i=0; i<(trackInLen>>1); i++) {
			tmp = trackIn[i];
			trackIn[i] = reversal_32bit(trackIn[trackInLen-1-i]);
			trackIn[trackInLen-1-i] = reversal_32bit(tmp);
		}
		if ((trackInLen&1) == 1) {
			tmp = trackIn[trackInLen>>1];
			trackIn[trackInLen>>1] = reversal_32bit(tmp);
		}
		reverse_done = 1;
		goto repeat;
	}
	else {
		return (NOT_GET_STX);
	}
}

/*
**	trackBuf[out] where decode data to be stored;
**		trackBuf[0]: track validate flag,
**			bit0: set 1 means that track1 decode succuss
**			bit1: set 1 means that track2 decode succuss
**			bit2: set 1 means that track3 decode succuss
**			bit4: set 1 means that track1 data presents
**			bit5: set 1 means that track2 data presents
**			bit6: set 1 means that track3 data presents
**		trackBuf[1]: valid data length amount in bytes for track1
**		trackBuf[2]: valid data length amount in bytes for track2
**		trackBuf[3]: valid data length amount in bytes for track3
**		trackBuf[4...]: valid data, stored with (track1 + track2 + track3)
**    trackBuf: trackBuf[0] + trackBuf[1] + trackBuf[2] + trackBuf[3] + '%' + track1 + '?' + ';' + track2 + '?' + ';' + track3 + '?'
**    note: the length of trackBuf >= 10
** RETURN:
**   the length of trackBuf
**
*/
#define DECODE_AGAIN_DONE 0xff
static uint32_t magc_decode(uint8_t *trackBuf, uint32_t trackBufLen)
{
	int32_t retVal;
	uint32_t index=4;
	uint32_t temp = 0;
	uint8_t i;

decode_again:
	index = 4;
	for (i=0; i<4; i++) {
		trackBuf[i] = 0;
	}

	if (TRACK_VALID(track1)) {
		trackBuf[0] |= 0x10;
		retVal = track1_decode(&tdata[track1].data[0], tdata[track1].bits_count/32 + 1, trackBuf+index, trackBufLen);
		if (retVal < 0) {
			trackBuf[index++] = '%';
			trackBuf[index++] = '?';
		}
		else {
			index += retVal;
			trackBuf[0] |= 0x01;
			trackBuf[1] = retVal;
		}
	}
	else {
		trackBuf[index++] = '%';
		trackBuf[index++] = '?';
	}

	if (TRACK_VALID(track2)) {
		trackBuf[0] |= 0x20;
		retVal = track23_decode(&tdata[track2].data[0], tdata[track2].bits_count/32 + 1, trackBuf+index, trackBufLen-index);
		if (retVal < 0) {
			trackBuf[index++] = ';';
			trackBuf[index++] = '?';
		}
		else {
			index += retVal;
			trackBuf[0] |= 0x02;
			trackBuf[2] = retVal;
		}
	}
	else {
		trackBuf[index++] = ';';
		trackBuf[index++] = '?';
	}

	if (TRACK_VALID(track3)) {
		trackBuf[0] |= 0x40;
		retVal = track23_decode(&tdata[track3].data[0], tdata[track3].bits_count/32 + 1, trackBuf+index, trackBufLen-index);
		if (retVal < 0) {
			trackBuf[index++] = ';';
			trackBuf[index++] = '?';
		}
		else {
			index += retVal;
			trackBuf[0] |= 0x04;
			trackBuf[3] = retVal;
		}
	}
	else {
		trackBuf[index++] = ';';
		trackBuf[index++] = '?';
	}

	if (trackAdaptDone || (temp == DECODE_AGAIN_DONE)) {
		track_flags = 0;
		return index;
	}

	if (trackBuf[0]&0x05){ /* track1 or track3 decode successful */
		track_flags = 0;
		trackAdaptDone = 1;
		return index;
	}
	else {
		temp = track1;
		track1 = track3;
		track3 = temp;
		temp = DECODE_AGAIN_DONE;
		goto decode_again;
	}

}



static ssize_t t1_magc_read(iodev_t *dev, void *buffer, size_t size)
{
#define TRACK_LEN 250
        unsigned char trackBuf[TRACK_LEN];
        int len;

        if (!TRACK_OK(track_flags))  {
            return 0;
        }
        magc_bits_decode();
        len = magc_decode(trackBuf, TRACK_LEN);

        if (size < len) len = size;
        memcpy(buffer, (void*)trackBuf, len);

        return len;
}

static int t1_magc_poll(iodev_t *dev, uint32_t flags)
{   
        if (flags & POLL_READ) {
            if (!TRACK_OK(track_flags)) {
                flags &= ~POLL_READ;
            }
        }

        return flags;
}

static int t1_magc_init(iodev_t *dev)
{
        MAGC->MAG_CTL = MAG_ADJ_MANUAL | MAG_EN; /* Enable MAG, Manual Adjustment */
        request_irq(IRQ_MAGCARD, magnetic_irq);
        MAGC->LPF_CTL = 0; /* Low Pass Filter Works normally */
        MAGC->PGA_CTL = 0; /* Programmable Gain Amplifier Works normally */
        MAGC->PKD_CTL = MAG_PKD_BYP | MAG_PKD_PWD; /* Peak Detector Bypass and Power Down */
        MAGC->CMP_CTL = 0; /* Comparator Works normally */
        MAGC->PWD_REF = 0; /* Vref power on */
        MAGC->REF_CTL = 1; /* Reference Voltage 2 */ 
        MAGC->PGA_GAIN = 0x242; /* 0x224 */ 
        MAGC->SAM_PARA = CONFIG_PCLK1 / CONFIG_MAGC_SAMPLING_CLK;
        MAGC->RST_PKD = 0x7;
        return 0;  
}
void magc_rest(void)
{
	SYSCFG->RST_SOFT1 |= MAG_PRST;
	mdelay(1);
	SYSCFG->RST_SOFT1 &= ~MAG_PRST;
	mdelay(5);
	t1_magc_init(NULL);
}


#define enable_magc_clk()	(SYSCFG->CLK_GATE0 |= MAG_CLK_EN)
#define disable_magc_clk()	(SYSCFG->CLK_GATE0 &= ~MAG_CLK_EN)

void powerdown_magc(void)
{
	MAGC->MAG_CTL = 0;
	MAGC->LPF_CTL = 0x3f;
	MAGC->PGA_CTL = 0x3f;
	MAGC->PKD_CTL = 0x3f;
	MAGC->CMP_CTL = 0x3f;
	MAGC->REF_CTL = 0;
	MAGC->PWD_REF = 1;
}


static int t1_magc_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
	switch (cmd) {
	case IOCTL_MAGC_START:
		enable_magc_clk();
		magc_rest();
		track_flags = 0;
		MAGC->INT_CLR = MAG_INT_BITS;
		MAGC->INT_EN = MAG_INT_BITS;
		break;
	case IOCTL_MAGC_STOP:
		powerdown_magc();
		disable_magc_clk();         
		break;
	case IOCTL_MAGC_PARAM:
		MAGC->REF_CTL = (uint32_t)arg;
		break;
	default:
		break;
	}
        return 0;
}

static const iodev_t t1_magc __iodev_entry = {
        .name = "magc",
        .priv = NULL,
        .init = NULL,
        .read = t1_magc_read,
        .write = NULL,
        .ioctl = t1_magc_ioctl,
        .poll = t1_magc_poll,
#ifdef CONFIG_POWER_CTL
        .suspend = NULL,
        .wakeup = NULL,
#endif
};

